package cn.work100.modular.layuiMsn.layimSocket;

import cn.work100.modular.layuiMsn.config.MyEndpointConfigure;
import cn.work100.modular.layuiMsn.entity.LayimGetMessages;
import cn.work100.modular.layuiMsn.entity.LayimSystemMessages;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;

@ServerEndpoint(value = "/layimSocket/{userid}",configurator = MyEndpointConfigure.class)
@Component
public class LayimSocket {

    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    private static Map<String, Session> map = new HashMap<String, Session>();
    //用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<LayimSocket> webSocketSet = new CopyOnWriteArraySet<LayimSocket>();
    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userid") String userid) throws IOException {
        map.put(userid, session);     //加入set中
        webSocketSet.add(this);
        addOnlineCount();           //在线数加1
        Map<String, Object> userList = (Map<String, Object>) redisTemplate.opsForValue().get("onlineUserList");
        if (userList == null) {
            userList = new HashMap<>();
            userList.put(userid, session.getId());
        }
        if (!userList.containsKey(userid))
            userList.put(userid, session.getId());
        redisTemplate.opsForValue().set("onlineUserList", userList);
        redisTemplate.opsForValue().set("onlineCount", getOnlineCount());
        LayimGetMessages messages = new LayimGetMessages();
        List<JSONObject> objectList = (List<JSONObject>) redisTemplate.opsForValue().get("offlineMessage" + userid);
        if (objectList != null)
            for (JSONObject s : objectList) {
                JSONObject data = s.getJSONObject("data");
                JSONObject to = data.getJSONObject("to");
                if (to.getInteger("id") == Integer.parseInt(userid)) {
                    JSONObject mine = data.getJSONObject("mine");
                    messages.setUsername(mine.getString("username"));
                    messages.setAvatar(mine.getString("avatar"));
                    messages.setId(mine.getInteger("id"));
                    messages.setType(to.getString("type"));
                    messages.setContent(mine.getString("content"));
                    messages.setMine(false);
                    messages.setFromid(mine.getInteger("id"));
                    messages.setCid("OffLine");
                    messages.setTimestamp(System.currentTimeMillis());
                    Session toSession = map.get(userid);
                    toSession.getAsyncRemote().sendText(JSON.toJSONString(messages));
                }
            }
        redisTemplate.delete("offlineMessage" + userid);
        Collection<Session> col = map.values();
        synchronized (col) {
            for (Session client : col) {
                client.getAsyncRemote().sendText("{\"noOnline\":" + userid + ",\"msg\":\"0\"}");
            }
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(@PathParam("userid") String userid, Session session) {
        Collection<Session> col = map.values();
        while (true == col.contains(session)) {
            col.remove(session);
        }
        webSocketSet.remove(this);
        subOnlineCount();           //在线数减1
        Map<String, Object> userList = (Map<String, Object>) redisTemplate.opsForValue().get("onlineUserList");
        Iterator<Map.Entry<String, Object>> it = userList.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<String, Object> entry = it.next();
            if(userid.equals(entry.getKey()))
                it.remove();
        }
        redisTemplate.opsForValue().set("onlineUserList", userList);
        synchronized (col) {
            for (Session client : col) {
                client.getAsyncRemote().sendText("{\"noOnline\":" + userid + "}");
            }
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("userid") String userid) throws IOException {
        // 获取在线用户
        Map<String, Object> userList = (Map<String, Object>) redisTemplate.opsForValue().get("onlineUserList");
        JSONObject jsonObject = JSONObject.parseObject(message);
        JSONObject data = jsonObject.getJSONObject("data");
        JSONObject mine = data.getJSONObject("mine");
        JSONObject to = data.getJSONObject("to");
        String toid = String.valueOf(to.getInteger("id"));
        // 用户在线时的操作
        if (map.get(toid) != null && userList.containsKey(toid)) {
            LayimGetMessages messages = new LayimGetMessages();
            messages.setUsername(mine.getString("username"));
            messages.setAvatar(mine.getString("avatar"));
            messages.setId(mine.getInteger("id"));
            messages.setType(to.getString("type"));
            messages.setContent(mine.getString("content"));
            messages.setMine(false);
            messages.setFromid(mine.getInteger("id"));
            messages.setTimestamp(System.currentTimeMillis());
            try {
                Session toSession = map.get(toid);
                toSession.getAsyncRemote().sendText(JSON.toJSONString(messages));
            } catch (Exception e) {
                onOpen(session, userid);
            }
        } else {
            LayimSystemMessages layimSystemMessages = new LayimSystemMessages();
            layimSystemMessages.setSystem(true);
            layimSystemMessages.setId(String.valueOf(jsonObject.get("tabid")));
            layimSystemMessages.setType("friend");
            layimSystemMessages.setContent("对方不在线，暂时无法接收消息<br>已存储到云端，上线后将提示");
            // 将离线消息存储到 redis
            List<JSONObject> objectList = (List<JSONObject>) redisTemplate.opsForValue().get("offlineMessage" + toid);
            if (objectList == null)
                objectList = new ArrayList<>();
            jsonObject.remove("tabid");
            objectList.add(jsonObject);
            redisTemplate.opsForValue().set("offlineMessage" + toid, objectList);
            Session toSession = map.get(userid);
            toSession.getAsyncRemote().sendText(JSON.toJSONString(layimSystemMessages));
        }
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) {
        session.getAsyncRemote().sendText(message);
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        LayimSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        LayimSocket.onlineCount--;
    }
}